#
# Copyright (C) [2024] Xingyun Integrated Circuit, Inc.
#
# GreenCode was a private technology asset of Xingyun Integrated Circuit， Inc （Confidential）
#Author: Shawn.Tan
#Date: 2025.10.28
#
# History: Initial Version 2025.10.28
#
#
from gpgpu.EnvGPGPU import EnvGPGPU
from gpgpu.GenThreadGPGPU import GenThreadGPGPU
from base.Sequence import Sequence


# This test verifies that unconditional jumps go to the expected address
# based on the register operand value.
class MainSequence(Sequence):
    def generate(self, **kargs):
        if self.getGlobalState("AppRegisterWidth") == 32:
            random_instructions = [
                "ADD##CALM",
                "SRLI#RV32I#CALM",
                "ADDI##CALM",
                "SLLI#RV32I#CALM",
                "LUI##CALM",
            ]
        else:
            random_instructions = [
                "ADDW##CALM",
                "SRLI#RV64I#CALM",
                "ADDI##CALM",
                "SLLI#RV64I#CALM",
                "LUI##CALM",
            ]
        branch_instr = "JALR##CALM"
        for _ in range(10):
            for _ in range(self.random32(0, 5)):
                self.genInstruction(self.choice(random_instructions))

            instr_id = self.genInstruction(branch_instr)
            instr_record = self.queryInstructionRecord(instr_id)

            # Skip instructions where the base register and destination
            # register are the same, as the base register will be overwritten
            # with the return address, preventing calculation of the expected
            # target address
            base_reg_index = instr_record["Addressing"]["Base"][0]
            dest_reg_index = instr_record["Dests"]["rd"]
            if base_reg_index != dest_reg_index:
                target_addr = instr_record["BRTarget"]
                expected_target_addr = self._getExpectedTargetAddress(
                    base_reg_index, instr_record["Addressing"]["Offset"][0]
                )

                if target_addr != expected_target_addr:
                    self.error(
                        "Unexpected target address: Expected = 0x%x, "
                        "Actual = 0x%x" % (expected_target_addr, target_addr)
                    )

                if self.getPEstate("PC") != expected_target_addr:
                    self.error(
                        "Unexpected PC value: Expected = 0x%x, Actual = 0x%x"
                        % (expected_target_addr, self.getPEstate("PC"))
                    )

    # Compute the expected target address for a JALR instruction given the
    # original PC value and the offset operand value.
    #
    #  @param aBaseRegIndex The base register index for the JALR instruction.
    #  @param aOffsetOprValue The offset operand for the JALR instruction.
    def _getExpectedTargetAddress(self, aBaseRegIndex, aOffsetOprValue):

        expected_target_addr = 0

        reg_name = "x%d" % aBaseRegIndex
        (reg_val, valid) = self.readRegister(reg_name)
        if not valid:
            self.error("Unable to read register %s" % reg_name)

        self.debug(
            "_getExpectedTargetAddress base reg X{:d}={:#x}, "
            "aOffsetOprValue:{:#x}".format(aBaseRegIndex, reg_val, aOffsetOprValue)
        )

        # process negative/positive 12b simm value | JALR sets bit 0 of
        # target to '0' after computing base+offset
        if (aOffsetOprValue & 0x800) != 0:
            offset = (aOffsetOprValue ^ 0xFFF) + 1
            expected_target_addr = (reg_val - offset) & ~1
        else:
            expected_target_addr = (reg_val + aOffsetOprValue) & ~1

        return expected_target_addr


MainSequenceClass = MainSequence
GenThreadClass = GenThreadGPGPU
EnvClass = EnvGPGPU
